//////////////////////////////////////////////////////////////////////////
//
// GAM203
// Textured Cube
// TexturedCube.cpp
// 
// Grant Johnson
// Qantm College
// 27/10/2010
//
//////////////////////////////////////////////////////////////////////////

#include "TexturedCube.h"
#include "assert.h"
#include <d3dx9.h>
#include <stdio.h>
#include "ShaderMgr.h"
#include "Shader.h"
#include "Renderer.h"

static const int VERTEX_COUNT = 6 * 4;  // Can't share verts at each corner because of UVs
static const int INDEX_COUNT = 36;

TexturedCube::TexturedCube()
: m_pTex(NULL)
{
	m_pTexFilename = new char[256];
	ZeroMemory(&m_mtl, sizeof(m_mtl));
}

TexturedCube::~TexturedCube()
{
	//Cleanup
	if(m_pTex)
	{
		m_pTex->Release();
		m_pTex = NULL;
	}
	if(m_pTexFilename)
	{
		delete m_pTexFilename;
		m_pTexFilename = NULL;
	}
}

bool TexturedCube::CreateObject(IDirect3DDevice9* a_pDevice, char* a_pTexFileName)
{
	//Ensure a valid device was passed in
	if(!a_pDevice)
	{
		return false;
	}

	

	//Copy of the texture path
	sprintf_s(m_pTexFilename, 256, "%s", a_pTexFileName);

	//Create the texture
	if(FAILED(D3DXCreateTextureFromFile(a_pDevice, m_pTexFilename, &m_pTex)))
	{
		return false;
	}

	//Create vertex buffer
	HRESULT result  = a_pDevice->CreateVertexBuffer(sizeof(VertexData) * VERTEX_COUNT, 0, VERTEX_FORMAT_FVF, D3DPOOL_DEFAULT, &m_pVertexBuffer, NULL);
	if(result != D3D_OK)
	{
		return false;
	}

	//Create index buffer
	result = a_pDevice->CreateIndexBuffer(sizeof(short) * INDEX_COUNT, 0, D3DFMT_INDEX16, D3DPOOL_DEFAULT, &m_pIndexBuffer, NULL);
	if(result != D3D_OK)
	{
		m_pVertexBuffer->Release();
		m_pVertexBuffer = NULL;
		return false;
	}

	//Setup the material
	m_mtl.Ambient = D3DXCOLOR(0.1f, 0.1f, 0.1f, 0.1f);
	m_mtl.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);

	//Setup the vertices of the cube
	if(!SetVertices())
	{
		return false;
	}

	//Setup the indices
	if(!SetIndices())
	{
		return false;
	}

	return true;
}

void TexturedCube::Render(float a_dt, IDirect3DDevice9* a_pDevice, const RenderParams& a_renderParams)
{
	if (m_pVertexBuffer && m_pIndexBuffer)
	{
		// Set our world matrix
		a_pDevice->SetTransform(D3DTS_WORLD, &m_worldMatrix);

		// Tell D3D about our vertex data
		a_pDevice->SetStreamSource(0, m_pVertexBuffer, 0, sizeof(VertexData));
		a_pDevice->SetFVF(VERTEX_FORMAT_FVF);

		a_pDevice->SetIndices(m_pIndexBuffer);

		const char* SHADER_NAME = "AdvancedShader";
		Shader* pShader = ShaderMgr::GetInstance().Find(SHADER_NAME);
		if(pShader)
		{
			ID3DXEffect* pEffect = pShader->GetEffect();
			if(pEffect)
			{
				pEffect->SetTechnique("AdvRenderDiff1Spec0Dir1Omni1Col");

				//Set material global variables
				pEffect->SetValue("g_materialAmbientColor", &D3DXCOLOR(m_mtl.Ambient), sizeof(D3DXCOLOR));
				pEffect->SetValue("g_materialDiffuseColor", &D3DXCOLOR(m_mtl.Diffuse), sizeof(D3DXCOLOR));
				pEffect->SetValue("g_materialSpecularColor", &D3DXCOLOR(m_mtl.Specular), sizeof(D3DXCOLOR));
				pEffect->SetValue("g_materialEmissiveColor", &D3DXCOLOR(m_mtl.Emissive), sizeof(D3DXCOLOR));
				pEffect->SetFloat("g_materialSpecularPower", m_mtl.Power);
				pEffect->SetFloat("g_materialSpecularIntensity", 1.0f);

				//Set texture global variable
				pEffect->SetTexture("g_diffuseTexture", m_pTex);
				pEffect->SetTexture("g_specularTexture", m_pTex);


				pEffect->SetValue("g_fogColor", &D3DXCOLOR(0.3f, 0.35f, 0.4f, 1.0f), sizeof(D3DXCOLOR));

				pEffect->SetFloat("g_fogDistStart", 10);
				pEffect->SetFloat("g_fogDistEnd", 200);
				pEffect->SetValue("g_cameraPos", Renderer::GetInstance().GetActiveCamera()->GetEyePos(), sizeof(D3DXVECTOR3));

				//Set up lighting global variables
				for(int lightId = 0; lightId < a_renderParams.GetLightCount(); ++lightId)
				{
					if(a_renderParams.GetLight(lightId)->Type == D3DLIGHT_DIRECTIONAL)
					{
						//Set lighting parameters
						//Note: Light direction is negated to optimise the dot product in 
						//shader calculations
						pEffect->SetValue("g_directionalDir", &(D3DXVECTOR3(a_renderParams.GetLight(lightId)->Direction) * -1.0f), sizeof(D3DXVECTOR3));
						pEffect->SetValue("g_directionalDiffuse", &D3DXCOLOR(a_renderParams.GetLight(lightId)->Diffuse), sizeof(D3DXCOLOR));
						pEffect->SetValue("g_directionalSpecular", &D3DXCOLOR(a_renderParams.GetLight(lightId)->Specular), sizeof(D3DXCOLOR));
					}
					else if(a_renderParams.GetLight(lightId)->Type == D3DLIGHT_POINT)
					{
						pEffect->SetValue("g_omniPos", &D3DXVECTOR3(a_renderParams.GetLight(lightId)->Position), sizeof(D3DXVECTOR3));
						pEffect->SetFloat("g_omniRange", a_renderParams.GetLight(lightId)->Range);
						pEffect->SetValue("g_omniDiffuse", &D3DXCOLOR(a_renderParams.GetLight(lightId)->Diffuse), sizeof(D3DXCOLOR));
						pEffect->SetValue("g_omniSpecular", &D3DXCOLOR(a_renderParams.GetLight(lightId)->Specular), sizeof(D3DXCOLOR));
					}
				}
				pEffect->SetValue("g_totalAmbient", &D3DXCOLOR(a_renderParams.GetGlobalAmbient()), sizeof(D3DXCOLOR));
				
				//Set matrix global variables
				//Calculate a combined world*view*proj matrix for the vertex shader
				D3DXMATRIX worldViewProj = m_worldMatrix*a_renderParams.GetViewMatrix()*a_renderParams.GetProjMatrix();
				pEffect->SetMatrix("g_worldViewProj", &worldViewProj);
				pEffect->SetMatrix("g_worldMat", &m_worldMatrix);

				//Render with shader
				UINT passCount = 0;
				if(pEffect->Begin(&passCount, 0) == D3D_OK)
				{
					for(UINT passIndex = 0; passIndex < passCount; ++passIndex)
					{		
						if(pEffect->BeginPass(passIndex) == D3D_OK)
						{
							//Draw the cube
							a_pDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, VERTEX_COUNT, 0, INDEX_COUNT / 3);
							pEffect->EndPass();
						}
					}

					pEffect->End();
				}
			}
		}
		
	}
}
bool TexturedCube::SetVertices()
{
	//Create set of vertices
	VertexData vertices[VERTEX_COUNT];

	//setup vertex positions
	//X axis
	//-1 = left side
	//+1 = right side

	//Y axis
	//-1 = bottom side
	//+1 = top side

	//Z axis
	//-1 = front side
	//+1 = back side

	const float CUBE_SIZE = 1.0f;

	m_aabb.GetMin() = D3DXVECTOR3(-CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE);
	m_aabb.GetMax() = D3DXVECTOR3(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE);


	//front
	vertices[0].pos = D3DXVECTOR3(-CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE); //front face, top left
	vertices[0].u = 0.0f;
	vertices[0].v = 0.0f;
	vertices[1].pos = D3DXVECTOR3(-CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE);//front face, bottom left
	vertices[1].u = 1.0f;
	vertices[1].v = 0.0f;
	vertices[2].pos = D3DXVECTOR3(CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE);//front face, top right
	vertices[2].u = 0.0f;
	vertices[2].v = 1.0f;
	vertices[3].pos = D3DXVECTOR3(CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE);//front face, bottom right
	vertices[3].u = 1.0f;
	vertices[3].v = 1.0f;
	
	//back
	vertices[4].pos = D3DXVECTOR3(-CUBE_SIZE, CUBE_SIZE, CUBE_SIZE); //back face, top left
	vertices[4].u = 0.0f;
	vertices[4].v = 0.0f;
	vertices[5].pos = D3DXVECTOR3(-CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE);//back face, bottom left
	vertices[5].u = 1.0f;
	vertices[5].v = 0.0f;
	vertices[6].pos = D3DXVECTOR3(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE);//back face, top right
	vertices[6].u = 0.0f;
	vertices[6].v = 1.0f;
	vertices[7].pos = D3DXVECTOR3(CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE);//back face, bottom right
	vertices[7].u = 1.0f;
	vertices[7].v = 1.0f;
	//left
	vertices[8].pos = D3DXVECTOR3(-CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE); //front face, top left
	vertices[8].u = 0.0f;
	vertices[8].v = 0.0f;
	vertices[9].pos = D3DXVECTOR3(-CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE);//front face, bottom left
	vertices[9].u = 1.0f;
	vertices[9].v = 0.0f;
	vertices[11].pos = D3DXVECTOR3(-CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE);//back face, bottom left
	vertices[10].u = 0.0f;
	vertices[10].v = 1.0f;
	vertices[10].pos = D3DXVECTOR3(-CUBE_SIZE, CUBE_SIZE, CUBE_SIZE); //back face, top left
	vertices[11].u = 1.0f;
	vertices[11].v = 1.0f;
	//right
	vertices[12].pos = D3DXVECTOR3(CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE);//front face, top right
	vertices[12].u = 0.0f;
	vertices[12].v = 0.0f;
	vertices[13].pos = D3DXVECTOR3(CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE);//front face, bottom right
	vertices[13].u = 1.0f;
	vertices[13].v = 0.0f;
	vertices[14].pos = D3DXVECTOR3(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE);//back face, top right
	vertices[14].u = 0.0f;
	vertices[14].v = 1.0f;
	vertices[15].pos = D3DXVECTOR3(CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE);//back face, bottom right
	vertices[15].u = 1.0f;
	vertices[15].v = 1.0f;
	//bottom
	vertices[19].pos = D3DXVECTOR3(CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE);//back face, bottom right
	vertices[16].u = 0.0f;
	vertices[16].v = 0.0f;
	vertices[17].pos = D3DXVECTOR3(CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE);//front face, bottom right
	vertices[17].u = 1.0f;
	vertices[17].v = 0.0f;
	vertices[18].pos = D3DXVECTOR3(-CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE);//back face, bottom left
	vertices[18].u = 0.0f;
	vertices[18].v = 1.0f;
	vertices[16].pos = D3DXVECTOR3(-CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE);//front face, bottom left
	vertices[19].u = 1.0f;
	vertices[19].v = 1.0f;
	//top
	vertices[21].pos = D3DXVECTOR3(CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE);//front face, top right
	vertices[20].u = 0.0f;
	vertices[20].v = 0.0f;
	vertices[23].pos = D3DXVECTOR3(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE);//back face, top right
	vertices[21].u = 1.0f;
	vertices[21].v = 0.0f;
	vertices[22].pos = D3DXVECTOR3(-CUBE_SIZE, CUBE_SIZE, CUBE_SIZE); //back face, top left
	vertices[22].u = 0.0f;
	vertices[22].v = 1.0f;
	vertices[20].pos = D3DXVECTOR3(-CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE); //front face, top left
	vertices[23].u = 1.0f;
	vertices[23].v = 1.0f;

	void* pLockedData = NULL;
	m_pVertexBuffer->Lock(0, sizeof(VertexData) * VERTEX_COUNT, &pLockedData, 0);
	memcpy(pLockedData, vertices, sizeof(VertexData) * VERTEX_COUNT);
	m_pVertexBuffer->Unlock();
	pLockedData = NULL;

	return true;
}
bool TexturedCube::SetIndices()
{
	//Create a data set of indices
	short indices[INDEX_COUNT];

	//front
	indices[0] = 0; 
	indices[1] = 2; 
	indices[2] = 1; 
	indices[3] = 1; 
	indices[4] = 2; 
	indices[5] = 3; 

	//left side
	indices[6] = 8; 
	indices[8] = 10; 
	indices[7] = 9; 
	indices[9] = 9; 
	indices[10] = 11; 
	indices[11] = 10; 

	//right side
	indices[12] = 12; 
	indices[13] = 14; 
	indices[14] = 13; 
	indices[15] = 13; 
	indices[16] = 14; 
	indices[17] = 15; 

	//top
	indices[18] = 20; 
	indices[19] = 22; 
	indices[20] = 21; 
	indices[21] = 21; 
	indices[22] = 22; 
	indices[23] = 23; 

	//back
	indices[24] = 4; 
	indices[25] = 5; 
	indices[26] = 6; 
	indices[27] = 6; 
	indices[28] = 5; 
	indices[29] = 7; 

	//bottom
	indices[30] = 17; 
	indices[31] = 19; 
	indices[32] = 16; 
	indices[33] = 19; 
	indices[34] = 18; 
	indices[35] = 16; 

	void* pLockedData = NULL;
	m_pIndexBuffer->Lock(0, sizeof(short) * INDEX_COUNT, &pLockedData, 0);
	memcpy(pLockedData, indices, sizeof(short) * INDEX_COUNT);
	m_pIndexBuffer->Unlock();
	pLockedData = NULL;

	return true;
}